CS 116: Introduction to Computer Science 2

CS 116 Tutorial 4 (Solutions): Lists, Mutation

Reminders:

  • Assignment 04 is due on Wed, Oct 9th at 10am

    Questions 1, 2, and 3 use the following data definition:

    A Card is a list of length 2 where
    - the first item is an integer between 1 and 13, inclusive, representing the value of the card, and
    - the second item is a string ("hearts", "spades", "clubs", or "diamonds") representing the suit of the card.
    Example: [1, "hearts"] represents the ace of hearts

  1. Write a function create_cards that consumes a list of card values (integers between 1 and 13), and a list of suite values (one of the four suit strings), and returns a list of cards created pair-wise from the consumed lists (values and suits).
    Example: create_cards([4,1,10], ["hearts","diamonds","clubs"]) => [ [4,"hearts"], [1, "diamonds"], [10, "clubs"] ].

    Solution:

    import check
    
    
    def create_cards(values, suits):
        '''returns a sorted list of cards with indicated values and suits. 
           The produced list has the same length as values and suits.
           
           create_cards: (listof Nat) 
                         (listof (anyof "hearts" "spades" "clubs" "diamonds")) 
                         -> (listof Card)
           Requires: len(values) == len(suits)
                     1 <= all elements of values <= 13
                     
           Examples: 
           create_cards([],[]) => []
           create_cards([4,1,10], ['hearts', 'diamonds', 'clubs']) 
               => [[1, 'diamonds'],[4,'hearts'], [10, 'clubs'] ]
        '''
        if values == []:
            return []
        else:
            return [ [values[0], suits[0]] ] + create_cards(values[1:], suits[1:])
    
    ## An alternative solution using map with 2 parameters
        
    def create_cards(values,suits):
        return list(map(lambda x, y:[x,y],values,suits))
            
    
    check.expect("cc1", create_cards([],[]), [])
    check.expect("cc2", create_cards([4], ['hearts']), [ [4,'hearts'] ])
    L = ['hearts', 'spades', 'spades', 'diamonds', 'clubs', 'hearts']
    check.expect("cc3", create_cards([6,2,5,4,3,1], L), 
                 [ [6, 'hearts'], [2,'spades'], [5,'spades'], [4, 'diamonds'], [3,'clubs'], [1,'hearts']])
    
  2. Write a function choose_by_colour that consumes a list of cards (hand) and a string "red" or "black" (colour) and returns a list of the values of the cards in hand of the appropriate colour (spades and clubs are "black", hearts and diamonds are "red").
    Example: choose_by_colour([[1,'hearts'], [9, 'spades'], [3,'diamonds']], 'red') => [1,3].

    Solution:

    import check
    
    
    def choose_by_colour(hand, colour):
        '''
        returns the list of values of the cards in hand which are of the specified 
        colour ('red' or 'black')
    
        choose_bv_colour: (listof Card) (anyof 'red' 'black') -> (listof Nat)
    
        Examples: 
        choose_by_colour([], 'red') => []
        choose_by_colour([[1,'hearts'], [9, 'spades'], [3,'diamonds']], 'red') => [1,3]
        '''
        if hand == []:
            return []
        if colour == 'red' and (hand[0][1]=='diamonds' or hand[0][1]=='hearts'):
            return [hand[0][0]] + choose_by_colour(hand[1:], colour)
        elif colour == 'black' and (hand[0][1]=='spades' or hand[0][1]=='clubs'):
            return [hand[0][0]] + choose_by_colour(hand[1:], colour)
        else:
            return choose_by_colour(hand[1:], colour)
    
    # Constants for testing    
    hearts3  = [3, 'hearts']
    clubs2 = [2, 'clubs']
    diamonds4 = [4, 'diamonds']
    spades9 = [9, 'spades']
    
    check.expect("cbc-t1", choose_by_colour([], 'red'), [])
    check.expect("cbc-t2", choose_by_colour([hearts3], 'red'), [3])
    check.expect("cbc-t3", choose_by_colour([clubs2], 'red'), [])
    check.expect("cbc-t4", choose_by_colour([diamonds4], 'red'), [4])
    check.expect("cbc-t5", choose_by_colour([spades9], 'red'), [])
    check.expect("cbc-t6", choose_by_colour([hearts3], 'black'), [])
    check.expect("cbc-t7", choose_by_colour([clubs2], 'black'), [2])
    check.expect("cbc-t8", choose_by_colour([diamonds4], 'black'), [])
    check.expect("cbc-t9", choose_by_colour([spades9], 'black'), [9])
    check.expect("cbc-t10", choose_by_colour([hearts3, clubs2, diamonds4, spades9], 'red'), [3,4])
    check.expect("cbc-t11", choose_by_colour([hearts3, clubs2, diamonds4, spades9], 'black'), [2,9])
    
  3. a) Write a function flip_colour that takes in a card, c, and mutates the suit of that card to a different colour: if c is a heart, it is mutated to a spade (and vice versa), while if c is a club, it is mutated to a diamond (and vice versa).

    b) Write a function flip_hand that reads in a list of cards (hand), and mutates the suit of each card in the list so that their colours are flipped in the same way as in flip_colour.

    Solution:

    a)

    # example definitions:
    diamond4 = [4,"diamonds"]
    heart3 = [3,'hearts']
    
    import check
    
    
    def flip_colour (c):
        '''
        mutates the suit of c: hearts becomes spades (and vice versa) and clubs 
        becomes diamonds (and vice versa)
        Effects: the suit field of c is mutated
    
        flip_colour: Card -> None
    
        Examples:
        if flip_colour (diamond4) is called then diamond4 will equal [4,'clubs']
        if flip_colour (heart3) is called then heart3 will equal [3,'spades']
        '''
        # Note that we cannot save c[1] as a local variable because it will
        # not alias the way we need; draw a memory diagram if this is unclear
        if c[1] == 'hearts':
            c[1] = 'spades'
        elif c[1] == 'spades':
            c[1] = 'hearts'
        elif c[1] == 'clubs':
            c[1] = 'diamonds'
        elif c[1] == 'diamonds':
            c[1] = 'clubs'
    
    # Tests:
    club4 = [4,"clubs"]
    check.expect("flip_colour Test 1 (function)", flip_colour(club4), None)
    check.expect("flip_colour Test 1 (card)", club4, [4,'diamonds'])
    
    diamond4 = [4,"diamonds"]
    check.expect("flip_colour Test 2 (function)", flip_colour(diamond4), None)
    check.expect("flip_colour Test 2 (card)", diamond4, [4,'clubs'])
    
    spade3 = [3,'spades']
    check.expect("flip_colour Test 3 (function)", flip_colour(spade3), None)
    check.expect("flip_colour Test 3 (card)", spade3, [3,'hearts'])
    
    heart3 = [3, 'hearts']
    check.expect("flip_colour Test 4", flip_colour (heart3), None)
    check.expect("flip_colour Test 4", heart3, [3,'spades'])
    

    b)

    import check
    
    
    def flip_hand_from(hand, position):
        '''
        Mutates the suits of all cards in hand from position to the end of the list
        Effects: Mutates the suit fields of all cards in hand[position:]
    
        flip_hand_from: (listof Card) Nat -> None
        '''
        if position < len(hand):
            flip_colour(hand[position])
            flip_hand_from(hand, position+1)
    
           
    def flip_hand(hand):
        '''
        mutates the suit of each card in hand: hearts becomes spades 
        (and vice versa) and clubs becomes diamonds (and vice versa)
        Effects: Mutates the suit field of each card in the list
        
        flip_hand_alt: (listof Card) -> None
    
        Examples:
        if lst = [] and flip_hand_alt(lst) is called, lst becomes []
        if lst = [[5, 'hearts'], [1, 'diamonds']] and flip_hand_alt(lst) is called,
        lst becomes [[5, 'spades], [1, 'clubs']]  
        '''
        flip_hand_from(hand, 0)
    
    # Tests:
    lst = []
    check.expect("flip_hand Test 1 (function)", flip_hand(lst), None)
    check.expect("flip_hand Test 1 (list)", lst, [])
    
    lst = [[7, 'spades']]
    check.expect("flip_hand Test 2 (function)", flip_hand(lst), None)
    check.expect("flip_hand Test 2 (list)", lst, [[7, 'hearts']])
    
    lst = [[6,'clubs'], [9,'hearts'], [2,'diamonds']]
    check.expect("flip_hand Test 3 (function)", flip_hand(lst), None)
    check.expect("flip_hand Test 3 (list)",
                 lst,
                 [[6,'diamonds'], [9,'spades'], [2,'clubs']])
    
  4. Write a function modify_list that consumes a list of integers (nums) and a single integer (n). The function returns None, but mutates the list in the following way:

    • If n does not appear in nums then add it to the end of nums.
    • If n appears once, then remove n from nums
    • If n appears at least twice, remove the first and last occurrences of n.

    For example:

    If L = [], modify_list(L,10) => None, and L = [10]
    If L = [1,2,3], modify_list(L,10) => None, and L = [1,2,3,10]
    If L = [1,2,3,4], modify_list(L,4) => None, and L = [1,2,3]
    If L = [1,5,2,1,7,1,9], modify_list(L,1) => None, and L = [0,5,2,1,7,0,9] 

    Solution:

    import check
    
    
    def index_of_last(vals, n, position):
        '''
        returns the index of the last occurrence of n in vals[:position+1], where
        0 <= position < len(vals)-1, and n occurs at least once in this range.
    
        index_of_last: (listof Int) Int Int -> Int
        Requires: position >= 0
        '''
        if vals[position]==n:
            return position
        else:
            return index_of_last(vals, n, position-1)
    
    def modify_list(nums, n):
        '''
        mutates nums to reflect the following conditions: 
        If n does not appear in nums, it is added to the end of it. If n appears exactly 
        once in the list, then remove it from nums. If n appears two of more times in nums, 
        the first and last occurrences of n is removed. 
        Effects: nums is mutuated
    
        modify_list: (listof Int) Int -> None
    
        Examples: 
        * if L = [], modify_list(L,10) => None, and L = [10]
        * if L = [1,2,3], modify_list(L,10) => None, and L = [1,2,3,10]
        * if L = [1,2,3,4], modify_list(L,4) => None, and L = [1, 2, 3]
        * if L = [1,5,2,1,7,1,9], modify_list(L,1) => None, and L = [5,2,1,7,9]
        Be sure to use list methods as part of your solution (though you can use recursion in 
        a helper function if needed).
        '''
        num_times = nums.count(n)
        if num_times==0:
            nums.append(n)
        elif num_times==1:
            nums.remove(n)
        else:
            first_place = nums.index(n)
            nums.pop(first_place)
            last_place = index_of_last(nums, n, len(nums)-1)
            nums.pop(last_place)
    
    # Tests:
    L = []
    check.expect("ml-1", modify_list(L, 3), None)
    check.expect("m1-1(L)", L, [3])
    check.expect("ml-2", modify_list(L,3), None)
    check.expect("ml-2(L)", L, [])
    L = [4,4]
    check.expect("ml-3", modify_list(L,4), None)
    check.expect("ml-3(L)", L, [])
    L = [-1,-1,-1]
    check.expect("ml-4", modify_list(L,-1), None)
    check.expect("ml-4(L)", L, [-1])
    L = [2,4,3,2,5,7,6,2,5,7]
    check.expect("ml-5", modify_list(L,2), None)
    check.expect("ml-5(L)", L, [4,3,2,5,7,6,5,7])
    check.expect("ml-6", modify_list(L,7), None)
    check.expect("ml-6(L)", L, [4,3,2,5,6,5])
    check.expect("ml-7", modify_list(L,0), None)
    check.expect("ml-7(L)", L, [4,3,2,5,6,5,0]) 
  5. Write a function sanitize that consumes a string, s, and returns a similar string but with any non-alphanumeric characters removed.
    For example: sanitize("@Test@") => "Test"

    Solution:

     import check
    
           
    def sanitize(s):
        '''
        returns a string like s, but with all non-alphanumeric characters removed from it.
    
        sanitize: Str -> Str
        
        Examples:
        sanitize("") => ""
        sanitize("Test") => "Test"
        sanitize("@Test@") => "Test"
        sanitize("!!@#$!") => ""
        '''
        return ''.join(list(filter(lambda x: x.isalpha(),s)))
    
    # Tests:
    check.expect("sanitize1", sanitize(""), "")
    check.expect("sanitize2", sanitize("Test"), "Test")
    check.expect("sanitize3", sanitize("Test!"), "Test")
    check.expect("sanitize4", sanitize("Hello World"), "HelloWorld")
    check.expect("sanitize5", sanitize("!@#$@TEST()()()123!@#!4"), "TEST1234")
    check.expect("sanitize6", sanitize("!@#$%^($)*@"), "")
    
    
  6. Write a function reversed_list a list of string, L, and returns a list containing the elements of L in reverse order. Write this function using abstract list functions.
    For example: reversed_list(["I","love","cs116"]) => ['cs116','love',I]

    Solution:

     import check
        
               
        def reversed_list(L):
        '''returns a list by reversing all elements in L.
               
           reversed_list: (listof Str) -> (listof Str)
                
           Examples:
           reversed_list([""]) => [""]
           reversed_list(["y"]) => ["y"]
           reversed_list(['I','love','cs116']) => ['cs116','love',I]
        '''
            return list(map(lambda i: L[-i], range(1,len(L)+1)))
        
    # Examples:
    check.expect("reversed_list_empty", reversed_list([""]), [""])
    check.expect("reversed_list single item", reversed_list(["y"]), ["y"])
    check.expect("reversed_list_short", reversed_list(['I','love','cs116']), ['cs116','love','I'])
    
    # Tests:
    check.expect("reversed_list_single item", rotate(["hahahaha"]), ["hahahaha"])
    check.expect("reversed_list_single_item_empty_str", rotate([""]), [""])
    check.expect("reversed_list_short", rotate(['1','2','3']), ['3','2','1'])
    check.expect("reversed_list_long",
                 rotate(['I','love','cs116','do','you','yes','you','do']), 
                 ['do', 'you', 'yes', 'you', 'do', 'cs116', 'love', 'I'])